import json
import os
import re
from datetime import datetime
from typing import Any
import redis
import requests
from langchain_core.callbacks import StreamingStdOutCallbackHandler
from langchain_core.tools import tool
from langchain_redis import RedisChatMessageHistory
from langchain.agents import create_tool_calling_agent, AgentExecutor
from langchain_core.messages import HumanMessage, AIMessage
from langchain_core.prompts import MessagesPlaceholder, ChatPromptTemplate
from langchain_openai import ChatOpenAI


# def clear_history(user_id: int, session_id: int) -> str:
def clear_history(user_id: int, session_id: int) -> bool:
    """
    根据用户ID和会话ID清除其聊天记录。
    :param session_id:
    :param user_id:
    :return: bool 返回是否删除结果
    """
    key = f"{user_id}+{session_id}"
    redis_client = redis.from_url(get_redisurl(), decode_responses=True)

    pattern = f"chat:{key}:*"
    # 使用scan方法查找匹配的键
    cursor = 0
    keys_to_delete = []

    while True:
        cursor, keys = redis_client.scan(cursor=cursor, match=pattern, count=100)
        if keys:
            keys_to_delete.extend(keys)
        if cursor == 0:
            break

    if keys_to_delete:
        redis_client.delete(*keys_to_delete)
        # return f"**系统消息** 已删除 {len(keys_to_delete)} 条聊天记录!"
        return True
    else:
        # return "**系统消息** 您的聊天记录为空!"
        return False


def view_chat_history(user_id: int, session_id: int) -> list:
    """
    查看指定用户和会话的聊天历史记录。
    :param user_id: 用户的唯一标识符
    :param session_id: 会话的唯一标识符
    :return
        list: 包含聊天记录的列表，每条记录为字典格式，包含角色和内容   如：
        [
            {
                'role': 'user',
                'content': '现在几点了'
            },
            {
                'role': 'ai',
                'content': '现在的时间是10:09:21。'
            },
            {
                'role': 'user',
                'content': '今天是几月几号'
            },
            {
                'role': 'ai',
                'content': '今天是2025年07月11日。'
            }
        ]
    """
    # 获取历史管理对象
    history_manager = get_history(user_id, session_id)
    # 获取所有消息
    messages = history_manager.messages

    # 将消息转换为列表
    chat_history = []
    for msg in messages:
        role = "user" if isinstance(msg, HumanMessage) else "ai"
        chat_history.append({
            "role": role,
            "content": msg.content
        })

    return chat_history


def get_history(user_id: int, session_id: int) -> RedisChatMessageHistory:
    """
    获取指定用户指定会话的历史记录
    :param user_id
    :param session_id
    :return: RedisChatMessageHistory 实例，用于管理该会话的聊天记录
    """
    key = f"{user_id}+{session_id}"
    return RedisChatMessageHistory(session_id=key, redis_url=get_redisurl())


def get_tongyi_api() -> str:
    """
    获取通义API密钥
    api_keys.json的相对路径： ./tools/api_url_messages.json
    """
    # 使用相对路径
    api_file_path = os.path.join(os.path.dirname(__file__), "tools", "api_url_messages.json")

    try:
        with open(api_file_path, "r", encoding="utf-8") as f:
            keys = json.load(f)
            return keys.get("tongyi_api_key", "")
    except FileNotFoundError:
        raise FileNotFoundError(f"找不到 API key 文件：{api_file_path}")


def get_redisurl() -> str:
    """
    获取Redis的URL
    api_keys.json的相对路径： ./tools/api_url_messages.json
    """
    # 使用相对路径
    api_file_path = os.path.join(os.path.dirname(__file__), "tools", "api_url_messages.json")

    try:
        with open(api_file_path, "r", encoding="utf-8") as f:
            keys = json.load(f)
            return keys.get("redis_url", "")
    except FileNotFoundError:
        raise FileNotFoundError(f"找不到 Redis URL 文件：{api_file_path}")


def get_api_base() -> str:
    """
    获取API的基础URL
    api_keys.json的相对路径： ./tools/api_url_messages.json
    """
    # 使用相对路径
    api_file_path = os.path.join(os.path.dirname(__file__), "tools", "api_url_messages.json")

    try:
        with open(api_file_path, "r", encoding="utf-8") as f:
            keys = json.load(f)
            return keys.get("api_base", "")
    except FileNotFoundError:
        raise FileNotFoundError(f"找不到 API base 文件：{api_file_path}")


@tool
def get_current_date() -> str:
    """获取当前的日期（格式：YYYY-MM-DD），无参数。"""
    return datetime.now().strftime('%Y-%m-%d')


@tool
def get_current_time() -> str:
    """获取当前的准确时间（格式：小时:分钟:秒），无需参数"""
    return datetime.now().strftime("%H:%M:%S")


@tool
def get_weather(city: str) -> str:
    """
    获取指定城市的天气信息。

    参数：
    city：要查询天气的城市名（中文），如 "北京"、"上海"、"广州"。
    返回：
    包含天气、温度、湿度、风向、空气质量等信息的描述文本。
    """
    api_url = 'http://apis.juhe.cn/simpleWeather/query'
    params_dict = {
        "city": city,
        "key": "e67ddb16981bae57f84bdc2516b8bcc5",
    }

    try:
        response = requests.get(api_url, params=params_dict)
        if response.status_code == 200:
            result = response.json()
            error_code = result.get('error_code')
            if error_code == 0 and 'result' in result:
                realtime = result['result']['realtime']
                temperature = realtime['temperature']
                humidity = realtime['humidity']
                info = realtime['info']
                direct = realtime['direct']
                power = realtime['power']
                aqi = realtime['aqi']
                return f"{city}当前天气：{info}，温度：{temperature}℃，湿度：{humidity}%，风向：{direct}，风力：{power}，空气质量指数：{aqi}"
            else:
                return f"请求失败:{result.get('error_code')} {result.get('reason')}"
        else:
            return f"请求异常，状态码：{response.status_code}"
    except Exception as e:
        return f"请求发生错误：{str(e)}"


@tool
def get_current_esports_news() -> str:
    """
    获取最新的电子竞技新闻（最多返回5条）。

    返回：
    按时间排列的简要电竞新闻标题列表。
    """
    api_url = 'http://apis.juhe.cn/fapigx/esports/query'
    apikey = '91c3e9d6549e17b16ad4d58133059e02'
    requestParams = {
        'key': apikey,
        'num': 5,
        'page': 1,
    }

    try:
        response = requests.get(api_url, params=requestParams)
        if response.status_code == 200:
            responseResult = response.json()
            if responseResult.get('error_code') == 0 and 'result' in responseResult:
                news_list = responseResult['result'].get('newslist', [])
                return '\n'.join([f"{item['ctime']}: {item['title']}" for item in news_list])
            else:
                return f"API返回错误：{responseResult.get('reason', '未知错误')}"
        else:
            return f"请求异常，状态码：{response.status_code}"
    except Exception as e:
        return f"请求发生错误：{str(e)}"


@tool
def get_recent_horoscopes(constellation: str) -> str:
    """
    获取指定星座今日的运势信息。

    参数：
    constellation：中文星座名称，如 "白羊座"、"金牛座"、"双子座" 等。
    返回：
    星座的综合、爱情、财富、健康等运势描述。
    """
    apiUrl = 'http://web.juhe.cn/constellation/getAll'
    apiKey = 'fd1d5d4f9571ad9dea59abccaee033b3'
    requestParams = {
        'key': apiKey,
        'consName': constellation,
        'type': 'today',
    }

    try:
        response = requests.get(apiUrl, params=requestParams)
        if response.status_code == 200:
            data = response.json()
            result_str = (
                f"星座：{data.get('name', '')}\n"
                f"日期：{data.get('datetime', '')}\n"
                f"综合：{data.get('all', '')}\n"
                f"爱情：{data.get('love', '')}\n"
                f"健康：{data.get('health', '')}\n"
                f"财富：{data.get('money', '')}\n"
                f"工作：{data.get('work', '')}\n"
                f"幸运色：{data.get('color', '')}，幸运数字：{data.get('number', '')}，速配星座：{data.get('QFriend', '')}\n"
                f"今日概述：{data.get('summary', '')}"
            )
            return result_str
        else:
            return f"请求异常，状态码：{response.status_code}"
    except Exception as e:
        return f"请求发生错误：{str(e)}"


def get_available_tools():
    """获取所有可用工具列表"""
    return [
        get_current_date,
        get_current_time,
        get_weather,
        get_current_esports_news,
        get_recent_horoscopes,
    ]


def create_chat_prompt():
    """创建聊天提示模板"""
    return ChatPromptTemplate.from_messages([
        (
            "你是一个聪明的AI助手，可以回答用户提出的问题、与用户聊天。注：你配备了一些可调用的工具(如get_current_time获取当前时间，get_current_date获取当前时间等)\n"
            "当用户向你向你提问时，比必须判断你要回答的是不是实时信息，（如当前时间、日期、天气、电子竞技新闻或星座运势等），"
            "当用户向你提问实时问题时（如当前时间、当天日期、某一城市的天气、近期的电子竞技新闻或星座运势等），你必须调用相应的工具获取最新信息,不能参考之前聊天记录进行回答，更不能自行估计。\n"
            "当用户向你提问其他问题时，你要给出准确、清晰的回答，不能应付\n"
        ),
        # 这是一个占位符，用于插入之前的对话历史（多轮对话），由 LangChain 自动管理。
        MessagesPlaceholder(variable_name="history"),
        ("human", "{input}"),
        # 插入工具调用过程，用于 agent 的思考链
        MessagesPlaceholder(variable_name="agent_scratchpad"),
    ])


class CustomStreamHandler(StreamingStdOutCallbackHandler):
    """自定义流式输出处理器，用于更好的格式化输出"""

    def __init__(self):
        super().__init__()
        self.text = ""  # 存储累积的文本

    def on_llm_new_token(self, token: str, **kwargs: Any) -> None:
        """每当模型生成新token时调用"""
        self.text += token
        print(token, end="", flush=True)  # 立即输出token并刷新缓冲区

    def get_accumulated_text(self) -> str:
        """获取累积的完整文本"""
        return self.text


class ChatAssistant:
    """
    """

    def __init__(self, api_base=get_api_base(),
                 model_name="qwen-plus", temperature=0.9, streaming=False):
        """
        初始化流式聊天助手

        参数:
            api_base: API基础URL
            model_name: 使用的模型名称
            temperature: 模型温度参数
        """
        # 获取API密钥
        self.api_key = get_tongyi_api()
        self.api_base = api_base
        self.model_name = model_name
        self.temperature = temperature
        self.streaming = streaming
        # 工具列表
        self.tools = get_available_tools()

        # 创建聊天提示词模板
        self.prompt = create_chat_prompt()

    def _create_chat_model(self):
        """创建的聊天模型"""
        return ChatOpenAI(
            openai_api_key=self.api_key,
            openai_api_base=self.api_base,
            model_name=self.model_name,
            temperature=self.temperature,
            streaming=self.streaming,
            model_kwargs={}
        )

    def chat(self, user_question: str, user_id: int, session_id: int) -> str:
        """
        与用户进行对话，管理历史记录

        参数:
            user_question: 用户的问题
            user_id: 用户ID
            session_id: 会话ID

        返回:
            str: AI的完整回答
        """
        # 获取历史管理对象
        history_manager = get_history(user_id, session_id)

        # # 特殊命令处理
        # if user_question.lower() == '**clear history':
        #     clear_history_message = clear_history(user_id, session_id)
        #     # 清理本地缓存的历史管理对象
        #     history_manager.clear()
        #     # 返回清除历史成功与否的消息
        #     return clear_history_message
        # elif user_question.lower() == '**view history':
        #     # 查看历史记录
        #     chat_history = view_chat_history(user_id, session_id)
        #     print("**系统消息** 正在查看聊天记录...")
        #     for record in chat_history:
        #         print(f"{record['role']}：{record['content']}")
        #     return "**系统消息** 以上为您的聊天记录！"

        # 获取原始历史
        raw_history = history_manager.messages

        # 过滤历史中的时间/日期类回答，防止模型复述旧时间
        filtered_history = self.filter_history_for_current_question(raw_history, user_question)

        # 创建模型
        model = self._create_chat_model()

        # 创建agent和执行器
        agent = create_tool_calling_agent(model, self.tools, self.prompt)
        agent_executor = AgentExecutor(
            agent=agent,
            tools=self.tools,
            # verbose=show_thinking,
            handle_parsing_errors=True
        )

        # 根据streaming参数决定是否使用流式输出
        if self.streaming:
            # 创建流式输出处理器
            stream_handler = CustomStreamHandler()

            # 调用模型获取流式回复
            result = agent_executor.invoke(
                {
                    "input": user_question,
                    "history": filtered_history
                },
                config={
                    "callbacks": [stream_handler]
                }
            )
            print()
            # 获取完整回复文本
            answer = result.get("output", stream_handler.get_accumulated_text())
        else:
            # 非流式输出
            result = agent_executor.invoke(
                {
                    "input": user_question,
                    "history": filtered_history
                }
            )

            # 获取完整回复
            answer = result.get("output", "")

        # 将用户消息和AI回复存入历史
        history_manager.add_message(HumanMessage(content=user_question))
        history_manager.add_message(AIMessage(content=answer))

        return answer

    @staticmethod
    def filter_history_for_current_question(history, current_input):
        """
        根据当前问题过滤历史记录中的时间/日期类内容（包括用户问题和AI回答）
        不修改原始 Redis 历史，仅过滤传入 Agent 的内容。

        参数:
            history: 原始历史消息列表 (包含HumanMessage/AIMessage等)
            current_input: 当前用户输入的问题

        返回:
            filtered: 过滤后的历史记录
        """

        # 定义时间/日期消息的关键特征（前缀匹配）
        time_indicators = [
            "现在的时间是", "当前时间是", "现在时刻是",
            "今天是", "当前日期是", "现在日期是",
            "时间是", "日期是", "现在时间：", "当前时间：",
            "今天日期：", "现在日期：", "当前日期："
        ]

        # 定义时间查询问题的关键词
        time_question_keywords = [
            "时间", "日期", "几点", "星期几", "今天几号", "几点了",
            "现在几点", "当前时间", "当前日期", "星期几了", "几号了", "几月几日", "几月几号"
        ]

        # 定义完整的时间查询模式
        complete_time_questions = [
            "现在几点", "今天几月几日", "今天星期几", "今天日期", "当前时间", "几点了",
            "日期是", "今天几号", "现在是几点", "几月几日", "现在时间"
        ]

        # 定义时间/日期消息的完整正则模式（完整匹配）
        time_patterns = [
            r"^\d{1,2}:\d{1,2}(:\d{1,2})?$",  # 时间格式 HH:MM 或 HH:MM:SS
            r"^\d{4}年\d{1,2}月\d{1,2}日$",  # 日期格式 YYYY年MM月DD日
            r"^星期[一二三四五六日]$"  # 星期格式
        ]

        filtered = []

        # 先预处理识别所有时间回答的索引及其对应的提问
        time_answers_indices = []
        time_questions_indices = []

        # 第一遍扫描：识别所有时间回答和对应的问题
        for i, message in enumerate(history):
            if message.__class__.__name__ == "AIMessage":
                content = message.content.strip()

                # 检查是否是时间/日期类消息
                is_time_message = False

                # 1. 检查是否包含时间指示前缀
                for indicator in time_indicators:
                    if content.startswith(indicator):
                        is_time_message = True
                        break

                # 2. 检查是否符合时间格式模式
                if not is_time_message:
                    for pattern in time_patterns:
                        if re.match(pattern, content):
                            is_time_message = True
                            break

                # 3. 特殊处理纯数字时间戳 (如：13:45)
                if not is_time_message and re.match(r"^\d{1,2}:\d{2}$", content):
                    is_time_message = True

                # 如果是时间消息，检查前一条是否是时间查询
                if is_time_message:
                    time_answers_indices.append(i)
                    # 检查前一条是否是用户时间查询
                    if i > 0 and history[i - 1].__class__.__name__ == "HumanMessage":
                        prev_content = history[i - 1].content.strip()
                        # 检查用户问题是否包含时间关键词
                        contains_time_keyword = False
                        for keyword in time_question_keywords:
                            if keyword in prev_content:
                                contains_time_keyword = True
                                break

                        if contains_time_keyword:
                            time_questions_indices.append(i - 1)

        # 额外检测孤立的时间查询问题
        for i, message in enumerate(history):
            if message.__class__.__name__ == "HumanMessage" and i not in time_questions_indices:
                content = message.content.strip().lower()
                # 检查是否是完整的时间查询
                for question in complete_time_questions:
                    if question in content:
                        time_questions_indices.append(i)
                        break

                # 如果没有匹配到完整问题，检查关键词组合
                if i not in time_questions_indices:
                    # 检查是否包含多个时间关键词
                    keyword_count = sum(1 for keyword in time_question_keywords if keyword in content)
                    if keyword_count >= 2:  # 如果包含2个或更多关键词
                        time_questions_indices.append(i)

        # 第二遍扫描：过滤消息
        for i, message in enumerate(history):
            # 如果索引在需要过滤的列表中，跳过
            if i in time_answers_indices:
                continue
            if i in time_questions_indices:
                continue

            # 保留所有未被过滤的消息
            filtered.append(message)

        return filtered
